#include "handler/handler.h"
#include "logger.h"

#include "handler/epoll.h"
#include "handler/command.h"
#include "handler/basic_command.h"
#include "handler/transfile.h"

#include "db/user.h"
#include "ds/loop_queue.h"

#include <sys/time.h>

// #define CHECK_TIME_OUT
#define TIME_OUT 180

int handleCommand(ThreadPool_t *pool, Client_t *client, MYSQL *conn)
{

    Command_t *cmd = CALLOC(Command_t, 1); // 接收客户端发送的命令
    int bytes_received;
    // 设置MSG_NOSIGNAL避免客户端断开导致服务端结束，设置MSG_WAITALL确保完整接收到了指令结构体
    bytes_received = recvAll(client->socket_fd, cmd, sizeof(Command_t), MSG_NOSIGNAL);
    if (bytes_received < 0)
    {
        PRINT_ERROR("Error in recv");
        LOG_WARNING("handleCommand", "Error in recv");
        free(cmd);
        return -1;
    }
    if (bytes_received == 0)
    {
        PRINT_INFO("Client disconnected");
        LOG_INFO("handleCommand", "Client disconnected");
        free(cmd);
        return -1;
    }

    // 设置命令结构体里用于记录对应客户端socket_fd，要放在recv后面
    cmd->client_fd = client->socket_fd;

    PRINT_DEBUG("Received command from client: %s %s %s",
                cmd->name, cmd->arg1, cmd->arg2);
    LOG_DEBUG("Handler", "Received command from client: %s %s %s",
              cmd->name, cmd->arg1, cmd->arg2);

    if (cmd->type == CMD_LOGIN)
    {
        handleLogin(client, conn);
        return 0;
    }
    else if (cmd->type == CMD_SIGNUP)
    {
        handleSignUp(client, conn);
        return 0;
    }

    if (cmd->type == CMD_DOWNLOAD)
    {
        PRINT_INFO("sendfile to client");
        // 在此构造一个file_info结构体作为参数传入,传入client结构体而不是cmd结构体
        submitTask(pool, clientDownloadFile, (void *)client);
    }
    else if (cmd->type == CMD_UPLOAD)
    {
        PRINT_INFO("recvfile from client");
        // 在此构造一个file_info结构体作为参数传入
        submitTask(pool, clientUploadFile, (void *)client);
    }
    else
    {
        Response_t resp;
        memset(&resp, 0, sizeof(Response_t));
        switch (cmd->type)
        {
        case CMD_QUIT:
            // PRINT_INFO("quit");
            // sleep(1);
            closeConnection(client);
            resp.if_success = true;
            break;
        case CMD_LS:
            lsCmd(&resp, client, conn);
            break;
        case CMD_LL:
            llCmd(&resp, client, conn);
            break;
        case CMD_CD:
            cdCmd(&resp, cmd->arg1, client, conn);
            break;
        case CMD_PWD:
            pwdCmd(&resp, client);
            break;
        case CMD_REMOVE:
            removeCmd(&resp, cmd, client, conn);
            break;
        case CMD_MV:
            mvCmd(&resp, cmd, client, conn);
            break;
        case CMD_MKDIR:
            mkdirCmd(&resp, cmd->arg1, client, conn);
            break;
        default:
            PRINT_INFO("invalid command, how can client send that?!");
            resp.if_success = false;
            break;
        }
        // 分别发送response结构体的固定部分和可变大小部分
        sendAll(cmd->client_fd, &resp, sizeof(resp) - sizeof(char *), 0);
        sendAll(cmd->client_fd, resp.resp, resp.resp_len, 0);
        LOG_DEBUG("Handler", "resp.if_success = %d", resp.if_success);
        // 发送完信息后记得释放可变长度的结构体成员
        if (resp.resp != NULL)
        {
            free(resp.resp);
            resp.resp = NULL;
        }
    }

    free(cmd);
    return 0;
}

int handleLogin(Client_t *client, MYSQL *conn)
{
    Response_t resp;
    memset(&resp, 0, sizeof(Response_t));
    // 创建用户
    UserInfo_t user;
    memset(&user, 0, sizeof(UserInfo_t));
    // 超时断开
    if (recvuser(&user, client) == -1)
    {
        resp.if_success = false;
        sendAll(client->socket_fd, &resp, sizeof(Response_t) - sizeof(char *), 0);
        return -1;
    }

    int ret_auth = authUser(conn, &user);
    // -1 验证失败：找不到对应用户、密码错误
    if (ret_auth == -1 || ret_auth == -2)
    {
        resp.if_success = false;
        sendAll(client->socket_fd, &resp, sizeof(Response_t) - sizeof(char *), 0);
        return -1;
    }
    memcpy(&client->user_info, &user, sizeof(UserInfo_t));

    resp.if_success = true;
    sendAll(client->socket_fd, &resp, sizeof(Response_t) - sizeof(char *), 0);

    sendAll(client->socket_fd, &user, sizeof(UserInfo_t), 0);
    LOG_DEBUG("Handler", "user: %s jwt: %s", user.name, user.jwt_token);

    return 0;
}

int handleSignUp(Client_t *client, MYSQL *conn)
{
    Response_t resp;
    memset(&resp, 0, sizeof(Response_t));

    // 创建用户
    UserInfo_t user;
    memset(&user, 0, sizeof(UserInfo_t));

    // 超时断开
    if (recvuser(&user, client) == -1)
    {
        resp.if_success = false;
        sendAll(client->socket_fd, &resp, sizeof(Response_t) - sizeof(char *), 0);
        return -1;
    }

    int ret_insert = insertUsers(conn, &user);
    // -2 注册失败
    if (ret_insert != 0)
    {
        resp.if_success = false;
        sendAll(client->socket_fd, &resp, sizeof(Response_t) - sizeof(char *), 0);
        return -2;
    }
    memcpy(&client->user_info, &user, sizeof(UserInfo_t));
    inserthome(conn, client);

    resp.if_success = true;
    sendAll(client->socket_fd, &resp, sizeof(Response_t) - sizeof(char *), 0);

    return 0;
}

void handleEpollEvent(ServerCluster_t *cluster, Server_t *server, ClientList_t *client_list, MYSQL *conn, ThreadPool_t *pool)
{
    int epoll_fd = epoll_create(1);
    epollAdd(epoll_fd, server->socket_fd);
    epollAdd(epoll_fd, STDIN_FILENO);

    int tunnel_epoll_fd = epoll_create(1);
    for (int i = 0; i < cluster->tunnel_count; i++)
    {
        epollAdd(tunnel_epoll_fd, cluster->tunnel->socket_fd);
    }

    LoopQueue_t *time_queue = createLoopQueue(TIME_OUT);
    for (int i = 0; i < TIME_OUT; i++)
    {
        int *time = CALLOC(int, (server->max_connections)); // time表示时间轮盘上的一个时间点 0-29s
        // 把time_queue->array[i]初始化为-1
        for (int j = 0; j < server->max_connections; j++)
        {
            time[j] = -1;
        }
        enqueueLoop(time_queue, time);
    }

    while (1)
    {
        struct epoll_event events[5];
        int event_num = epoll_wait(epoll_fd, events, 5, 1000);

#ifdef CHECK_TIME_OUT
        // 检测时间轮盘中是否有客户端超时
        time_t current_time = time(NULL);
        int time_seconds = (int)current_time % 30;
        for (int i = 0; i < server->max_connections; i++)
        {
            if (time_queue->array[time_seconds][i] != -1)
            {
                // 超时断开
                // PRINT_INFO("close %d:%d", time_seconds, time_queue->array[time_seconds][i]);
                PRINT_INFO("Client-%d 超时断开", i);
                LOG_INFO("Handler", "Client-%d 超时断开", i);
                closeConnection(&client_list->clients[i]);
                // 从时间轮盘中移除该客户端
                time_queue->array[time_seconds][i] = -1;
            }
        }
#endif

        for (int i = 0; i < event_num; i++)
        {
            int cfd = events[i].data.fd;  // 就绪fd
            if (cfd == server->socket_fd) // 接收新连接
            {
                for (int n = 0; n < server->max_connections; n++)
                {
                    // 找到一个空的客户端结构体接收连接
                    if (client_list->clients[n].socket_fd == -1)
                    {
                        // 接收新连接
                        acceptConnection(server, &client_list->clients[n]);
                        // 发送新的隧道连接，现在默认发送第一个隧道
                        sendAll(client_list->clients[n].socket_fd, &cluster->tunnel[0], sizeof(Tunnel_t), 0);
                        // 接收隧道连接
                        acceptTunnelConnection(&cluster->tunnel[0], &client_list->clients[n]);
                        time_t current_time = time(NULL);
                        int time_seconds = (int)current_time % 30;
                        // 将该客户端socket_fd放入时间轮盘对应的时间点
                        time_queue->array[time_seconds][n] = client_list->clients[n].socket_fd;
                        // PRINT_INFO("login %d:%d", time_seconds, time_queue->array[time_seconds][n]);
                        // 放入监听
                        epollAdd(epoll_fd, client_list->clients[n].socket_fd);

                        break;
                    }
                }
            }
            else if (cfd == STDIN_FILENO) // 接收服务端输入指令
            {
                char input[256];
                if (fgets(input, sizeof(input), stdin) != NULL)
                {
                    // 去除换行符
                    size_t len = strlen(input);
                    if (len > 0 && input[len - 1] == '\n')
                    {
                        input[len - 1] = '\0';
                    }

                    if (strcmp(input, "shutdown") == 0)
                    {
                        PRINT_INFO("Server shutting down...");
                        LOG_INFO("Handler", "Server shutting down...");
                        destroyLoopQueue(time_queue);
                        return;
                    }
                    else
                    {
                        PRINT_INFO("Invalid command! %s", input);
                    }
                }
                else
                {
                    perror("Error reading input");
                }
            }
            else // 说明有客户端的socket就绪
            {
                for (int n = 0; n < server->max_connections; n++)
                {
                    // 找到对应的客户端信息结构体
                    if (cfd == client_list->clients[n].socket_fd)
                    {
                        // 更新时间轮盘中该客户端所在的时间点

                        if (handleCommand(pool, &client_list->clients[n], conn) != 0)
                        {
                            // 从时间轮盘中移除该客户端
                            for (int i = 0; i < TIME_OUT; i++)
                            {
                                for (int j = 0; j < server->max_connections; j++)
                                {
                                    if (time_queue->array[i][j] == cfd)
                                    {
                                        time_queue->array[i][j] = -1;
                                    }
                                }
                            }

                            epollDel(epoll_fd, cfd);
                            closeConnection(&client_list->clients[n]);
                        }
                        else
                        {
                            // 从时间轮盘中移除该客户端之前所在的位置
                            for (int i = 0; i < TIME_OUT; i++)
                            {
                                for (int j = 0; j < server->max_connections; j++)
                                {
                                    if (time_queue->array[i][j] == cfd)
                                    {
                                        time_queue->array[i][j] = -1;
                                        // PRINT_INFO("old %d:%d", i, time_queue->array[i][j]);
                                    }
                                }
                            }
                            time_t current_time = time(NULL);
                            int time_seconds = (int)current_time % 30;
                            time_queue->array[time_seconds][n] = cfd;
                            // PRINT_INFO("new %d:%d", time_seconds, time_queue->array[time_seconds][n]);
                        }
                        break;
                    }
                }
            }
        }
    }
}
